# -*- coding: utf-8 -*-
import os
import json
import enum
import decimal
import datetime
from app.models.base import BaseModel
from flask.json.provider import JSONProvider
from pydantic import BaseModel as PydanticModel

from flask import Flask

from app.config import config
from flask_cors import CORS


def create_app():
    app = Flask(__name__)
    # 基础配置
    app.config.from_object(config[os.getenv('FLASK_ENV', 'local')])
    # 设置参数
    CORS(app, supports_credentials=True)
    # 扩展
    config_extensions(app)
    # 注册蓝图
    register_blueprints(app)
    # 配置json编码器
    configure_json_decode(app)
    return app


def configure_json_decode(app):
    app.json_provider_class = CustomJSONProvider
    app.json = app.json_provider_class(app)


class CustomJSONProvider(JSONProvider):
    def default(self, obj):
        try:
            if isinstance(obj, BaseModel):
                return obj.to_dict()
            if isinstance(obj, PydanticModel):
                return obj.dict()
            elif isinstance(obj, enum.Enum):
                return obj.value
            elif isinstance(obj, datetime.date):
                return datetime.datetime(obj.year, obj.month, obj.day).isoformat()
            elif isinstance(obj, datetime.time):
                return str(obj)
            elif isinstance(obj, decimal.Decimal):
                return float(obj)
            elif isinstance(obj, datetime.timedelta):
                return int(obj.total_seconds() * 1000)
            else:
                return str(obj)
        except TypeError:
            pass
        return super().default(obj)

    def dumps(self, obj, **kwargs):
        kwargs.setdefault('ensure_ascii', False)
        return json.dumps(obj, default=self.default, **kwargs)

    def loads(self, s, **kwargs):
        return json.loads(s, **kwargs)


def config_extensions(app):
    """
    配置扩展
    """
    from app.extensions import db
    db.init_app(app)
    db.app = app


def register_blueprints(app):
    # 导入blueprint对象而不是api对象
    from app.controller import site, conversation, collect, menu
    app.register_blueprint(site)
    app.register_blueprint(conversation)
    app.register_blueprint(collect)
    app.register_blueprint(menu)
